package com.startx.core.netty.handler;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

import org.apache.log4j.Logger;

import com.startx.core.config.holder.ConfigHolder;
import com.startx.core.netty.output.wss.WebSocketOutput;
import com.startx.core.point.anotation.RequestMethod;
import com.startx.core.point.anotation.ResponseType;
import com.startx.core.point.factory.PointFactory;
import com.startx.core.system.model.AccessPointTarget;
import com.startx.core.system.model.ChannelFactory;
import com.startx.core.system.model.StartxConfig;
import com.startx.core.tools.JsonTool;
import com.startx.core.tools.XmlWriter;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class WebsocketHandler extends SimpleChannelInboundHandler<Object> {

	private WebSocketServerHandshaker handshaker;
	private static final Logger Log = Logger.getLogger(WebsocketHandler.class);
	private StartxConfig config = ConfigHolder.getConfig();

	/**
	 * 当客户端连接成功，返回个成功信息
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		//可以进行channel注册
	}

	/**
	 * 当客户端断开连接
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		ChannelFactory.logout(ctx);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

		// http：//xxxx
		if (msg instanceof FullHttpRequest) {

			handleHttpRequest(ctx, (FullHttpRequest) msg);
		} else if (msg instanceof WebSocketFrame) {
			// ws://xxxx
			handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
		}

	}

	//websocket消息处理（只支持文本）
	public void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {

		// 关闭请求
		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}

		// ping请求
		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}

		// 只支持文本格式，不支持二进制消息
		if (!(frame instanceof TextWebSocketFrame)) {
			throw new Exception("仅支持文本格式");
		}

		// 客服端发送过来的消息
		String body = ((TextWebSocketFrame) frame).text();
		Map<String, Object> data = JsonTool.json2map(body);

		Log.debug(data);
		/**
		 * 协议格式
		 * {
		 * 	"subject":"/common",
		 *  "command":"/login",
		 *  "udid":"6666666666666",
		 *  "param":{
		 *  	"username":"foobar",
		 *  	"password":"cykbdj"
		 *  }
		 * }
		 */
		// 如果是注册命令则执行Channel注册
		Object udid = data.get("udid");
		if (Objects.isNull(udid)) {
			String udids = udid.toString();
			if (!ChannelFactory.isLogin(udids)) {
				ChannelFactory.login(udids, ctx);
			}
		} else {
			ctx.close();
			return;
		}

		String subject = data.get("subject").toString();
		String command = data.get("command").toString();
		String uri     = subject+command;
		AccessPointTarget target = 
				PointFactory.getAccessPoint(RequestMethod.WEBSOCKET.name()+"_"+uri);
		
		if(Objects.isNull(target)) {
			ctx.close();
			return;
		}
		
		Method method = target.getMethod();
		
		// 调用方法参数
		Object output = method.invoke(target.getObj(),data.get("param"));
		// 根据类型写返回值
		ifReturnValueThenOutput(ctx, target.getType(), output);
	}
	
	/**
	 * 输出数据
	 */
	public void ifReturnValueThenOutput(ChannelHandlerContext ctx, ResponseType type, Object output)
			throws UnsupportedEncodingException {
		if (output != null) {
			if (type == ResponseType.JSON) {
				WebSocketOutput.push(ctx, JsonTool.obj2json(output));
			} else if (type == ResponseType.XML) {
				WebSocketOutput.push(ctx, XmlWriter.startXml().writeObject(output).endXml());
			}
		}
	}

	// 第一次请求是http请求，请求头包括ws的信息
	public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request)
			throws Exception {

		if (!request.decoderResult().isSuccess()) {
			ctx.close();
			Log.error("一次奇怪的调用：ip：" + ctx.channel().remoteAddress());
			return;
		}

		String uri = request.uri();

		String websocketUri = config.getEndPoint().equals("/")?"":config.getEndPoint()+config.getWebsocket();
		if (uri.equals(websocketUri)) {
			//建立websocket连接
			WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
					"wss:/" + ctx.channel().localAddress() + config.getEndPoint(), null, false);
			handshaker = wsFactory.newHandshaker(request);
			if (handshaker == null) {
				// 不支持
				WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
			} else {
				handshaker.handshake(ctx.channel(), request);
			}
			
			return;
		} else {
			// 处理普通http请求
			new HttpHandler().handleHttpRequest(ctx,request);
		}
	}

	// 异常处理，netty默认是关闭channel
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {

			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state() == IdleState.READER_IDLE) {
				// 读数据超时
			} else if (event.state() == IdleState.WRITER_IDLE) {
				// 写数据超时
			} else if (event.state() == IdleState.ALL_IDLE) {
				// 通道长时间没有读写，服务端主动断开链接
				ctx.close();
			}

		} else {
			super.userEventTriggered(ctx, evt);
		}
	}

}
